package com.zh.search.service.impl;

import cn.hutool.core.util.StrUtil;
import com.zh.search.domain.dto.*;
import com.zh.search.enums.SortField;
import com.zh.search.service.IEsService;
import com.zh.web.base.PageApiResult;
import com.zh.web.domain.media.vo.ArticleVo;
import com.zh.web.domain.media.vo.VideoVo;
import com.zh.web.enums.SortType;
import com.zh.web.enums.Status;
import com.zh.web.openApi.ArticleClient;
import com.zh.web.openApi.VideoClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class EsServiceImpl implements IEsService {

    private final ElasticsearchRestTemplate elasticsearchRestTemplate;
    private final ArticleClient articleClient;

    private final VideoClient videoClient;

    public EsServiceImpl(ElasticsearchRestTemplate elasticsearchRestTemplate, ArticleClient articleClient, VideoClient videoClient) {
        this.elasticsearchRestTemplate = elasticsearchRestTemplate;
        this.articleClient = articleClient;
        this.videoClient = videoClient;
    }

    @Override
    public PageApiResult<ArticleVo> searchArticleFromEs(ArticleSearchDto articleSearchDto) {
        String keyword = articleSearchDto.getKeyword();
        //构造查询条件
        BoolQueryBuilder boolQueryBuilder = this.getPublicBoolQuery(articleSearchDto);
        if (StrUtil.isNotEmpty(keyword)) {
            boolQueryBuilder.should(QueryBuilders.matchQuery("content", keyword));
            boolQueryBuilder.should(QueryBuilders.matchQuery("subTitle", keyword));
            boolQueryBuilder.minimumShouldMatch(1);
        }
        // 构造排序
        SearchSourceBuilder searchSourceBuilder = this.getPublicSort(articleSearchDto);
        //分页
        PageRequest pageRequest = this.getPublicPageRequest(articleSearchDto);
        // 构造查询
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder)
                .withPageable(pageRequest).withSorts(searchSourceBuilder.sorts()).build();
        SearchHits<ArticleEsDto> searchHits = elasticsearchRestTemplate.search(searchQuery, ArticleEsDto.class);
        //构造结果
        if (searchHits.hasSearchHits()) {
            List<SearchHit<ArticleEsDto>> searchList = searchHits.getSearchHits();
            List<Long> idList = searchList.stream()
                    .map(searchHit -> searchHit.getContent().getId())
                    .collect(Collectors.toList());
            List<ArticleVo> articleVoList = articleClient.listArticle(idList).getData();
            return PageApiResult.<ArticleVo>builder()
                    .pageNum(articleSearchDto.getPageNum())
                    .pageSize(articleSearchDto.getPageSize())
                    .list(articleVoList)
                    .build();
        }
        return PageApiResult.<ArticleVo>builder()
                .pageNum(articleSearchDto.getPageNum())
                .pageSize(articleSearchDto.getPageSize())
                .list(new ArrayList<>())
                .build();
    }

    @Override
    public PageApiResult<VideoVo> searchVideoFromEs(VideoSearchDto videoSearchDto) {
        //构造查询条件
        BoolQueryBuilder boolQueryBuilder = this.getPublicBoolQuery(videoSearchDto);
        SearchSourceBuilder searchSourceBuilder = this.getPublicSort(videoSearchDto);
        //分页
        PageRequest pageRequest = this.getPublicPageRequest(videoSearchDto);
        // 构造查询
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder)
                .withPageable(pageRequest).withSorts(searchSourceBuilder.sorts()).build();
        SearchHits<VideoEsDto> searchHits = elasticsearchRestTemplate.search(searchQuery, VideoEsDto.class);
        //构造结果
        if (searchHits.hasSearchHits()) {
            List<SearchHit<VideoEsDto>> searchList = searchHits.getSearchHits();
            List<Long> idList = searchList.stream()
                    .map(searchHit -> searchHit.getContent().getId())
                    .collect(Collectors.toList());
            List<VideoVo> videoVoList = videoClient.listArticle(idList).getData();
            return PageApiResult.<VideoVo>builder()
                    .pageNum(videoSearchDto.getPageNum())
                    .pageSize(videoSearchDto.getPageSize())
                    .list(videoVoList)
                    .build();
        }
        return PageApiResult.<VideoVo>builder()
                .list(Collections.emptyList())
                .pageNum(videoSearchDto.getPageNum())
                .pageSize(videoSearchDto.getPageSize())
                .build();
    }

    private BoolQueryBuilder getPublicBoolQuery(SearchDto searchDto) {
        //es分页默认从0开始
        String keyword = searchDto.getKeyword();
        Long createOperator = searchDto.getCreateOperator();
        LocalDateTime startTime = searchDto.getStartTime();
        LocalDateTime endTime = searchDto.getEndTime();
        // 关键词检索
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StrUtil.isNotBlank(keyword)) {
            boolQueryBuilder.should(QueryBuilders.matchQuery("title", keyword));
            boolQueryBuilder.should(QueryBuilders.matchQuery("author", keyword));
            boolQueryBuilder.should(QueryBuilders.matchQuery("summary", keyword));
            boolQueryBuilder.should(QueryBuilders.termQuery("lawType", keyword));
            boolQueryBuilder.should(QueryBuilders.termQuery("creationType", keyword));
        }
        // 所属人检索
        if (null != createOperator) {
            boolQueryBuilder.should(QueryBuilders.termQuery("createOperator", createOperator));
        }
        //时间范围检索
        if (null != startTime) {
            boolQueryBuilder.should(QueryBuilders.rangeQuery("createTime").gte(startTime));
            boolQueryBuilder.should(QueryBuilders.rangeQuery("updateTime").gte(startTime));
        }
        // 一定是状态正常
        boolQueryBuilder.should(QueryBuilders.termQuery("status", Status.ENABLE));
        if (null != endTime) {
            boolQueryBuilder.should(QueryBuilders.rangeQuery("createTime").lte(endTime));
            boolQueryBuilder.should(QueryBuilders.rangeQuery("updateTime").lte(endTime));
        }
        boolQueryBuilder.minimumShouldMatch(1);
        return boolQueryBuilder;
    }

    private SearchSourceBuilder getPublicSort(SearchDto searchDto) {
        SortType sortType = searchDto.getSortType();
        SortField sortField = searchDto.getSortField();
        //排序 默认分数降序
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        SortOrder sortOrder = SortType.ASC.equals(sortType) ? SortOrder.ASC : SortOrder.DESC;
        if (null == sortField) {
            SortBuilder<?> sortBuilder = SortBuilders.scoreSort().order(sortOrder);
            searchSourceBuilder.sort(sortBuilder);
        } else {
            FieldSortBuilder fieldSortBuilder = new FieldSortBuilder(sortField.getField()).order(sortOrder);
            searchSourceBuilder.sort(fieldSortBuilder);
        }
        return searchSourceBuilder;
    }

    private PageRequest getPublicPageRequest(SearchDto searchDto) {
        long pageNum = searchDto.getPageNum() - 1L;
        Long pageSize = searchDto.getPageSize();
        //分页
        return PageRequest.of((int) pageNum, pageSize.intValue());
    }
}
